#include <types.h>
#include <random.h>
#include <errno.h>

#define delay_s(n)	// deubg
#define mem_addr_inc(type, pos, start, end)		for (pos = (type *)(start); pos <= (type *)(end); pos++)
#define mem_addr_dec(type, pos, start, end)		for (pos = (type *)(start); pos >= (type *)(end); pos--)

/* March-PS(4N) with random pair multibackground */
int memtest_random_marchps(uint32_t start, uint32_t end, uint32_t *err_addr)
{
	uint32_t r, m, i;
	uint32_t *p;

	for (i = 0; i < 6000; i++) {
		srand(i / 2);
		mem_addr_inc(uint32_t, p, start, end) {
			*p = (i & 1)? ~rand(): rand();
		}

		srand(i / 2);
		mem_addr_inc(uint32_t, p, start, end) {
			r = (i & 1)? ~rand(): rand();
			for (m = 1; m != 0; m <<= 1) {
				if (*p != r) {
					err_addr = p;
					return -EFAULT;
				}
				*p = r = r ^ m;
				if (*p != r) {
					err_addr = p;
					return -EFAULT;
				}
			}
		}
	}
	return 0;
}

/* March-TBW algorithm */
int memtest_marchtbw(uint32_t start, uint32_t end, uint32_t *err_addr)
{
	uint32_t d, nd, b, nb;
	uint32_t *p;

	d = 0;
	nd = ~d;

	/* M0 */
	mem_addr_inc(uint32_t, p, start, end) {
		*p = d;
	}

	/* M1 */
	mem_addr_inc(uint32_t, p, start, end) {
		if (*p != d) {
			goto find_error;
		}
		*p = nd;
	}

	/* M2 */
	mem_addr_inc(uint32_t, p, start, end) {
		if (*p != nd) {
			goto find_error;
		}
	}

	/* M3 */
	mem_addr_inc(uint32_t, p, start, end) {
		if (*p != nd) {
			goto find_error;
		}
		*p = d;
	}
	
	/* M4 */
	mem_addr_inc(uint32_t, p, start, end) {
		if (*p != d) {
			goto find_error;
		}
	}

	/* M5 */
	mem_addr_dec(uint32_t, p, end, start) {
		if (*p != d) {
			goto find_error;
		}
		*p = nd;
	}

	/* M6 */
	mem_addr_dec(uint32_t, p, end, start) {
		if (*p != nd) {
			goto find_error;
		}
	}

	/* M7 */
	mem_addr_dec(uint32_t, p, end, start) {
		if (*p != nd) {
			goto find_error;
		}
		*p = d;
	}

	delay_s(60 * 5);

	/* M8 */
	mem_addr_dec(uint32_t, p, end, start) {
		if (*p != d) {
			goto find_error;
		}
	}

	/* M9 */
	mem_addr_dec(uint32_t, p, end, start) {
		*p = nd;
	}

	delay_s(60 * 5);

	/* M10 */
	mem_addr_dec(uint32_t, p, end, start) {
		if (*p != nd) {
			goto find_error;
		}
	}

	b = 1;
	nb = ~b;
	for (b = 1, nb = ~b; b != 0; b <<= 1, nb = ~b) {
		/* M11 */
		mem_addr_inc(uint32_t, p, start, end) {
			*p = d;
		}

		/* M12 */
		mem_addr_inc(uint32_t, p, start, end) {
			*p = b;
			if (*p != b) {
				goto find_error;
			}
		}

		/* M13 */
		mem_addr_dec(uint32_t, p, end, start) {
			*p = nd;
		}

		/* M14 */
		mem_addr_dec(uint32_t, p, end, start) {
			*p = nb;
			if (*p != nb) {
				goto find_error;
			}
		}

		/* M15 */
		mem_addr_inc(uint32_t, p, start, end) {
			*p = b;
			if (*p != b) {
				goto find_error;
			}
			*p = nb;
			if (*p != nb) {
				goto find_error;
			}
		}
	}

	return 0;

find_error:
	*err_addr = (uint32_t)p;
	return -EFAULT;
}
